#include "big_integer.hpp"

#include <iostream>
#include <fstream>
#include <cstdarg>

using namespace std;

typedef BIG_NUMBERS::uinteger_short_vector_ff uinteger;

const char *program_name = "rsa";

void error_exit(const char *format, ...);
void usage();

void read_key(const char *fln, uinteger& key, uinteger& mode);
void read_message(uinteger& message);

int main(int argc, char **argv)
{
        if (argc != 2)
        {
                usage();
        }
        program_name = argv[0];
        uinteger key, mod, message;

        read_key(argv[1], key, mod);
        read_message(message);

        uinteger rsa = message.modexp(key, mod);
        cout << (string)rsa;
        return 0;
}


void usage()
{
        cerr << "Usage:\n\t" << program_name << " <key-file-name>\n";
        exit(1);
}

void error_exit(const char *format, ...)
{
        va_list va;
        va_start(va, format);
        int size = vsnprintf(0, 0, format, va);
        if (size > 0)
        {
                size++; // vsnprintf does not count zero-termination
                auto_ptr<char> buf(new char[size]);
                vsnprintf(buf.get(), size, format, va);
                cerr << buf.get() << endl;
        }
        va_end(va);
        exit(1);
}

void assign_from_string(const string& str, uinteger& u)
{
        try { u = str; }
        catch (invalid_digit&)
        { error_exit("Invalid number <%s>", str.c_str()); }
}

void read_key(const char *fln, uinteger& key, uinteger& mod)
{
        ifstream key_file(fln);
        if (!key_file.good())
        { error_exit("Can't access key file: %s\n", fln); }

        string key_string, mode_string;

        if (!key_file.good())
        { error_exit("Incorrect key file format: can't read key"); }
        getline(key_file, key_string);

        if (!key_file.good())
        { error_exit("Incorrect key file format: can't read mode"); }
        getline(key_file, mode_string);

        if (key_string.empty() || mode_string.empty())
        { error_exit("Corrupted key file %s", fln); }

        // now we have both strings
        assign_from_string(key_string, key);
        assign_from_string(mode_string, mod);
}

void read_message(uinteger& message)
{
        string str;
        getline(cin, str);
        if (str.empty()) { error_exit("Empty message"); }

        assign_from_string(str, message);
}
